module fsm (pop_clk, pop_rst_n, pop_req_n, fifo_empty, pop_wd_n, part_wd, pop_error);

localparam 	  IN_WIDTH    = 24;
localparam	  OUT_WIDTH   = 8;
localparam 	  K 	      = IN_WIDTH/OUT_WIDTH;
localparam 	  COUNT_WIDTH = $clog2 (K);
localparam 	  BYTE_ORDER  = 1;
parameter 	  ERR_MODE    = 0;
parameter    	  IDLE 	      = 0,
		  POPPING = 1,
		  POPPED = 2,
		  ERROR = 3;

input 		  pop_clk;
input 		  pop_rst_n;
input 		  pop_req_n;
input 		  fifo_empty;

output reg 	  part_wd;
output reg 	  pop_error;
output 		  pop_wd_n;

reg 	  [3:0]   state_reg, state_next;
reg 		  part_wd_next, pop_error_next;
wire 	  [COUNT_WIDTH-1:0] count_out;

dti_counter #(IN_WIDTH, OUT_WIDTH, BYTE_ORDER)
  dti_counter_inst (pop_clk, pop_rst_n, pop_req_n, fifo_empty, count_out);

assign pop_wd_n = (count_out == K-1) ? 1'b0 : 1'b1;

//state register
always @ (posedge pop_clk or pop_rst_n)
  if (!pop_rst_n) begin
    state_reg <= 4'b0;
    state_reg [IDLE] <= 1'b1;
  end
  else
    state_reg <= state_next;

//data register
always @ (posedge pop_clk or pop_rst_n)
  if (!pop_rst_n) begin
    part_wd <= 0;
    pop_error <= 0;
  end 
  else begin
    part_wd <= part_wd_next;
    pop_error <= pop_error_next;
  end

//state transitions
always @ (pop_req_n, fifo_empty, state_reg)
begin
  state_next = 4'b0;
  case (1'b1)
    state_reg [IDLE]: if (!pop_req_n && fifo_empty) //IDLE state
      state_next [ERROR] = 1'b1;
    else if (!pop_req_n)
      state_next [POPPING] = 1'b1;
    else
      state_next [IDLE] = 1'b1;
    
    state_reg [POPPING]: if (fifo_empty) //POPPING state
      state_next [ERROR] = 1'b1;
    else if (count_out == K-1)
      state_next [POPPED] = 1'b1;
    else
      state_next [POPPING] = 1'b1;
    
    state_reg [POPPED]: if (!pop_req_n && fifo_empty) //POP completed word
      state_next [ERROR] = 1'b1;
    else if (!pop_req_n)
      state_next [POPPING] = 1'b1;
    else
      state_next [IDLE] = 1'b1;
    
    state_reg [ERROR]:; //ERROR state
  endcase
end

//registering outputs
always @ (posedge pop_clk or pop_rst_n)
begin
  if (!pop_rst_n) begin
    part_wd_next <= 0;
    pop_error_next <= 0;
  end
  else begin
    part_wd_next <= 0;
    pop_error_next <= 0;
    case (1'b1)
      state_next [IDLE]:;
      state_next [POPPING]: part_wd_next <= 1'b1;
      state_next [POPPED]: part_wd_next <= 1'b0;
      state_next [ERROR]: pop_error_next <= 1'b1;
    endcase
  end
end

endmodule